(function($) {
	var resizable = function(els, opts) {
		var $this = $(els), helper = null;
		$.extend(this, {
			init : init,
			disabled : function(e) {
				$this.unbind("mousemove", setDirection);
				$this.unbind("mousedown", startResize);
			}
		});
		function init() {
			$this.bind("mousemove", setDirection);
			$this.bind("mousedown", startResize);
		}
		function getDirection(e) {
			var dir = "";
			var place = $this.offset();
			var width = $this.outerWidth();
			var height = $this.outerHeight();
			var edge = opts.edge;
			if (e.pageY > place.top && e.pageY < place.top + edge) {
				dir += "n";
			} else {
				if (e.pageY < place.top + height
						&& e.pageY > place.top + height - edge) {
					dir += "s";
				}
			}
			if (e.pageX > place.left && e.pageX < place.left + edge) {
				dir += "w";
			} else {
				if (e.pageX < place.left + width
						&& e.pageX > place.left + width - edge) {
					dir += "e";
				}
			}
			var handle = opts.handles.split(",");
			for ( var i = 0; i < handle.length; i++) {
				var hand = handle[i].replace(/(^\s*)|(\s*$)/g, "");
				if (hand == "all" || hand == dir) {
					return dir;
				}
			}
			return "";
		}
		function setDirection(e) {
			var dir = getDirection(e);
			if (dir == "") {
				$this.css("cursor", "default");
			} else {
				$this.css("cursor", dir + "-resize");
			}
		}
		function startResize(e) {
			opts.onStartResize.call($this, e);
			var dir = getDirection(e);
			if (dir == "")
				return;
			$this.unbind("mousemove", setDirection);
			$this.css("-moz-user-select", "none");
			$this.attr("unselectable", "on");
			var data = {
				target : $this,
				dir : dir,
				startLeft : getCssValue('left'),
				startTop : getCssValue('top'),
				left : getCssValue('left'),
				top : getCssValue('top'),
				startX : e.pageX,
				startY : e.pageY,
				startWidth : $this.outerWidth(),
				startHeight : $this.outerHeight(),
				width : $this.outerWidth(),
				height : $this.outerHeight(),
				deltaWidth : $this.outerWidth() - $this.width(),
				deltaHeight : $this.outerHeight() - $this.height()
			};
			if (opts.helper) {
				helper = $this.clone().empty().appendTo($("body"));
				helper.css( {
					background : "",
					display : "none"
				});
				helper.css(opts.helperStyle);
				data.target = helper;
			}
			$(document).bind("mousemove", data, resize);
			$(document).bind("mouseup", data, stop);
		}
		function doResize(e) {
			helper.css("display", "block");
			var rData = e.data;
			if (rData.dir.indexOf('e') != -1) {
				var width = rData.startWidth + e.pageX - rData.startX;
				width = Math.min(Math.max(width, opts.doWsize[0]),
						opts.doWsize[1]);
				rData.width = width;
			}
			if (rData.dir.indexOf('s') != -1) {
				var height = rData.startHeight + e.pageY - rData.startY;
				height = Math.min(Math.max(height, opts.doHsize[0]),
						opts.doHsize[1]);
				rData.height = height;
			}
			if (rData.dir.indexOf('w') != -1) {
				w = rData.startWidth - e.pageX + rData.startX;
				if (w >= opts.doWsize[0] && w <= opts.doWsize[1]) {
					rData.width = w;
					rData.left = rData.startLeft + e.pageX - rData.startX;
				}
			}
			if (rData.dir.indexOf('n') != -1) {
				h = rData.startHeight - e.pageY + rData.startY;
				if (h >= opts.doHsize[0] && h <= opts.doHsize[1]) {
					rData.height = h;
					rData.top = rData.startTop + e.pageY - rData.startY;
				}
			}
		}
		function applySize(e) {
			var rData = e.data;
			if ($.boxModel == true) {
				rData.target.css( {
					width : rData.width - rData.deltaWidth,
					height : rData.height - rData.deltaHeight,
					left : rData.left,
					top : rData.top
				});
			} else {
				rData.target.css( {
					width : rData.width,
					height : rData.height,
					left : rData.left,
					top : rData.top
				});
			}
			opts.onResize.call($this, e);
		}
		function resize(e) {
			doResize(e);
			applySize(e);
			return false;
		}
		function stop(e) {
			if (opts.helper) {
				e.data.target = $this;
				helper.remove();
				applySize(e);
			}
			$(document).unbind("mousemove", resize);
			$(document).unbind("mouseup", stop);
			$this.bind("mousemove", setDirection);
			opts.onStopResize.call($this, e);
			return false;
		}
		function getCssValue(css) {
			var val = parseInt($this.css(css));
			if (isNaN(val)) {
				return 0;
			} else {
				return val;
			}
		}
	};
	$.fn.resizable = function(options) {
		var pir = this.eq(typeof options == 'number' ? options : 0).data(
				"resizable");
		if (pir) {
			return pir;
		}
		this.each(function() {
			var opts = $.extend( {}, $.fn.resizable.defaults, {}, options);
			pir = new resizable(this, opts);
			pir.init();
			$(this).data("resizable", pir);
		});
		return pir;
	};
	$.fn.resizable.defaults = {
		handles : "n,e,s,w,se,all",
		helper : false,
		helperStyle : null,
		doWsize : [ 10, 10000 ],
		doHsize : [ 10, 10000 ],
		edge : 5,
		onStartResize : function() {
		},
		onResize : function() {
		},
		onStopResize : function() {
		}
	};
})(jQuery);